package p2p

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

type TunnelInfo struct {
	Uid        string
	Sid        int32 //>= 0
	LocalPort  uint16
	RemotePort uint16
	PortIndex  int32 // >=0
	Tag        string
}

var p2pErrorDescribe = map[int32]string{
	-30014:"device off line",
	-30019:"max session",
}
func getError(err int32)string{
	if str,ok :=p2pErrorDescribe[err]; ok{
		return str
	}
	return ""
}

type Agent struct {
}

func NewAgent() *Agent {
	return &Agent{}
}

func (*Agent) AgentInit(key string, maxCon uint32) bool {
	if result := SetLicenseKey(key); result != NoERROR {
		fmt.Printf("SetLicenseKey res %d \n", result)
		return false
	}
	fmt.Println("Set_License_Key ok")

	if result := AgentInitialize(maxCon); result != NoERROR {
		fmt.Printf("AgentInitialize res %d \n", result)
		return false
	}
	fmt.Println("AgentInitialize ok")
	return true
}

func (agent *Agent) BlindTunnel(infos []*TunnelInfo) {
	for _, info := range infos {
		agent.Blind(info)
		fmt.Println("sid ", info.Sid)
	}
}

func (*Agent) Blind(info *TunnelInfo)(bool) {

	var sId  int32 = -1
	if info.LocalPort == 0{
		for i := 0 ; i < 3; i++{
			rand.Seed(time.Now().UnixNano())
			info.LocalPort = uint16(rand.Intn(40000) + 10000)
			fmt.Printf("### Blind %s random port %d\n",info.Uid, info.LocalPort)
			sId = AgentConnect(info.Uid)
			if sId >= 0{
				break
			}
			if sId != -30002{ //port is using
				continue
			}else{
				break
			}
		}
	}else{
		fmt.Printf("### Blind %s exist port %d\n",info.Uid, info.LocalPort)
		sId = AgentConnect(info.Uid)
	}

	if sId < 0 {
		fmt.Printf("Error Blind %s %d , sid %d %s\n" , info.Uid, info.LocalPort,sId, getError(sId))
		return false
	}
	//fmt.Println(info.Tag, " connect success", sId)
	info.Sid = sId

	//set 5m buffer
	if result := SetBufSize(sId, 512000000); result != NoERROR {
		fmt.Printf(info.Tag, " SetBufSize res %d \n", result)
		return false
	}
	//fmt.Println(info.Tag, " SetBufSize success")

	if info.PortIndex = PortMapping(sId, info.LocalPort, info.RemotePort); info.PortIndex < 0 {
		fmt.Printf("PortMapping %s %d => %d error : %d %s\n",info.Uid, info.LocalPort, info.RemotePort,info.PortIndex ,getError(info.PortIndex))
		return false
	}
	//fmt.Printf("Blind %s %d ok\n",info.Uid, info.LocalPort)
	time.Sleep(time.Second * 1)
	//fmt.Printf(info.Tag, " PortMapping success %s %d \n",info.Uid, info.LocalPort)
	return true
}

func (agent *Agent) Service(infos []*TunnelInfo, finish *bool, wg *sync.WaitGroup) {
	for {
		for _, info := range infos {
			if info.Sid >= 0 {
				//get state
				access_time := LastIOTime(info.Sid)
				if access_time < 0 {
					fmt.Printf("%s LastIOTime err uid: %s access_time %d\n", info.Tag, info.Uid, access_time)
				} else {
					fmt.Printf("%s LastIOTime ok  uid: %s  \n", info.Tag, info.Uid)
				}

				//TODO 处理reconnect

			}
		}
		if *finish {
			break
		}
		time.Sleep(time.Second * 1)
	}
	for _, info := range infos {
		agent.DisConnect(info)
	}
	fmt.Println("service finish")
	wg.Done()
}

func (*Agent) DisConnect(info *TunnelInfo) {
	fmt.Printf("%s dis connect %s %d\n", info.Tag, info.Uid, info.Sid)

	if info.Sid >= 0 {
		AgentDisConnect(info.Sid)
		PortStopMapping(info.PortIndex)
		info.Sid = -1
	}
}
func (a*Agent) Close() {
}
